﻿// Decompiled with JetBrains decompiler
// Type: TaleWorlds.CampaignSystem.QuestManager
// Assembly: TaleWorlds.CampaignSystem, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: E85F8C15-4DF6-4E9C-A58A-29177E40D07A
// Assembly location: D:\steam\steamapps\common\Mount & Blade II Bannerlord\bin\Win64_Shipping_Client\TaleWorlds.CampaignSystem.dll

using System;
using System.Collections.Generic;
using TaleWorlds.CampaignSystem.Actions;
using TaleWorlds.CampaignSystem.GameMenus;
using TaleWorlds.CampaignSystem.Issues;
using TaleWorlds.CampaignSystem.MapEvents;
using TaleWorlds.CampaignSystem.Party;
using TaleWorlds.CampaignSystem.Settlements;
using TaleWorlds.CampaignSystem.Settlements.Locations;
using TaleWorlds.Core;
using TaleWorlds.Library;
using TaleWorlds.SaveSystem;

#nullable disable
namespace TaleWorlds.CampaignSystem
{
  public class QuestManager : CampaignEventReceiver
  {
    public const string QuestOfferToken = "quest_offer";
    public const string QuestDiscussToken = "quest_discuss";
    public const string HeroMainOptionsToken = "hero_main_options";
    public const string NpcLordStartToken = "lord_start";
    public const string CharacterTalkToken = "start";
    public static string PriorQuestName;
    private MBList<QuestBase> _currentHourlyTickQuestsToTimeout;
    [SaveableField(0)]
    private readonly MBList<QuestBase> _quests;
    [SaveableField(10)]
    private readonly Dictionary<ITrackableCampaignObject, List<QuestBase>> _trackedObjects;

    internal static void AutoGeneratedStaticCollectObjectsQuestManager(
      object o,
      List<object> collectedObjects)
    {
      ((QuestManager) o).AutoGeneratedInstanceCollectObjects(collectedObjects);
    }

    protected virtual void AutoGeneratedInstanceCollectObjects(List<object> collectedObjects)
    {
      collectedObjects.Add((object) this._quests);
      collectedObjects.Add((object) this._trackedObjects);
    }

    internal static object AutoGeneratedGetMemberValue_quests(object o)
    {
      return (object) ((QuestManager) o)._quests;
    }

    internal static object AutoGeneratedGetMemberValue_trackedObjects(object o)
    {
      return (object) ((QuestManager) o)._trackedObjects;
    }

    public MBReadOnlyList<QuestBase> Quests => (MBReadOnlyList<QuestBase>) this._quests;

    public bool QuestDebugMode { get; set; }

    public MBReadOnlyDictionary<ITrackableCampaignObject, List<QuestBase>> TrackedObjects { get; private set; }

    public QuestManager()
    {
      this._quests = new MBList<QuestBase>();
      this._trackedObjects = new Dictionary<ITrackableCampaignObject, List<QuestBase>>();
      this._currentHourlyTickQuestsToTimeout = new MBList<QuestBase>();
      this.Initialize();
    }

    [LoadInitializationCallback]
    private void OnLoad(MetaData metaData) => this.Initialize();

    private void Initialize()
    {
      this.TrackedObjects = this._trackedObjects.GetReadOnlyDictionary<ITrackableCampaignObject, List<QuestBase>>();
      this._currentHourlyTickQuestsToTimeout = new MBList<QuestBase>();
    }

    public override void OnQuestStarted(QuestBase quest) => this._quests.Add(quest);

    public bool IsThereActiveQuestWithType(Type type)
    {
      foreach (QuestBase quest in (List<QuestBase>) this.Quests)
      {
        if (quest.IsOngoing && (type == quest.GetType() || quest.GetType().IsSubclassOf(type)))
          return true;
      }
      return false;
    }

    public bool IsQuestGiver(Hero offeringHero)
    {
      foreach (QuestBase quest in (List<QuestBase>) this.Quests)
      {
        if (quest.IsOngoing && quest.QuestGiver == offeringHero)
          return true;
      }
      return false;
    }

    public override void OnGameLoaded(CampaignGameStarter campaignGameStarter)
    {
      List<QuestBase> questBaseList = new List<QuestBase>();
      for (int index = this.Quests.Count - 1; index >= 0; --index)
      {
        QuestBase quest = this.Quests[index];
        if (quest == null)
          this._quests.Remove(quest);
        else if (!quest.IsFinalized)
        {
          bool flag = false;
          foreach (KeyValuePair<Hero, IssueBase> issue in Campaign.Current.IssueManager.Issues)
          {
            if (issue.Value?.IssueQuest == quest)
            {
              flag = true;
              break;
            }
          }
          if (flag || quest.IsSpecialQuest)
          {
            quest.InitializeQuestOnLoadWithQuestManager();
            foreach (QuestTaskBase task in (List<QuestTaskBase>) quest.TaskList)
            {
              if (task.IsActive)
              {
                task.SetReferences();
                task.AddTaskDialogs();
              }
            }
          }
          else
          {
            questBaseList.Add(quest);
            Debug.FailedAssert("There is not active issue for quest: " + (object) quest.Title + " string id: " + quest.StringId + ". Quest will be canceled.", "C:\\Develop\\MB3\\Source\\Bannerlord\\TaleWorlds.CampaignSystem\\QuestManager.cs", nameof (OnGameLoaded), (int) sbyte.MaxValue);
          }
        }
      }
      foreach (QuestBase questBase in questBaseList)
        questBase.CompleteQuestWithCancel();
    }

    public override void OnSessionStart(CampaignGameStarter campaignGameStarter)
    {
      CampaignEvents.MapEventStarted.AddNonSerializedListener((object) this, new Action<MapEvent, PartyBase, PartyBase>(((CampaignEventReceiver) this).OnMapEventStarted));
    }

    public override void HourlyTick()
    {
      for (int index = this.Quests.Count - 1; index >= 0; --index)
      {
        QuestBase quest = this.Quests[index];
        if (quest.IsOngoing && quest.QuestDueTime.IsPast)
          this._currentHourlyTickQuestsToTimeout.Add(quest);
      }
      foreach (QuestBase questBase in (List<QuestBase>) this._currentHourlyTickQuestsToTimeout)
      {
        if (!questBase.IsFinalized)
          questBase.CompleteQuestWithTimeOut();
      }
      this._currentHourlyTickQuestsToTimeout.Clear();
      for (int index = this.Quests.Count - 1; index >= 0; --index)
        this.Quests[index].HourlyTickWithQuestManager();
    }

    public override void DailyTick()
    {
    }

    public GameMenuOption.IssueQuestFlags CheckQuestForMenuLocations(List<Location> currentLocations)
    {
      GameMenuOption.IssueQuestFlags issueQuestFlags = GameMenuOption.IssueQuestFlags.None;
      foreach (Location currentLocation in currentLocations)
      {
        foreach (LocationCharacter character in currentLocation.GetCharacterList())
        {
          Hero heroObject = character.Character?.HeroObject;
          if (heroObject != null)
          {
            foreach (QuestBase quest in (List<QuestBase>) this.Quests)
            {
              if (quest != null && quest.IsOngoing)
              {
                if (quest.QuestGiver == heroObject)
                  issueQuestFlags |= quest.IsSpecialQuest ? GameMenuOption.IssueQuestFlags.ActiveStoryQuest : GameMenuOption.IssueQuestFlags.ActiveIssue;
                else if (quest.IsTracked((ITrackableCampaignObject) heroObject))
                  issueQuestFlags |= quest.IsSpecialQuest ? GameMenuOption.IssueQuestFlags.TrackedStoryQuest : GameMenuOption.IssueQuestFlags.TrackedIssue;
              }
            }
          }
        }
      }
      foreach (Location currentLocation in currentLocations)
        issueQuestFlags |= this.IsLocationsTracked(currentLocation);
      return issueQuestFlags;
    }

    private GameMenuOption.IssueQuestFlags IsLocationsTracked(Location location)
    {
      GameMenuOption.IssueQuestFlags issueQuestFlags = GameMenuOption.IssueQuestFlags.None;
      foreach (QuestBase quest in (List<QuestBase>) this.Quests)
        issueQuestFlags |= quest.IsLocationTrackedByQuest(location);
      return issueQuestFlags;
    }

    public void OnQuestFinalized(QuestBase quest) => this._quests.Remove(quest);

    public override void OnPlayerCharacterChanged(
      Hero oldPlayer,
      Hero newPlayer,
      MobileParty newPlayerParty,
      bool isMainPartyChanged)
    {
      for (int index = this.Quests.Count - 1; index >= 0; --index)
      {
        QuestBase quest = this.Quests[index];
        if (quest.IsOngoing && !quest.IsSpecialQuest)
          quest.CompleteQuestWithFail();
      }
    }

    public override void CanHaveQuestsOrIssues(Hero hero, ref bool result)
    {
      foreach (QuestBase quest in (List<QuestBase>) this.Quests)
      {
        if (quest.IsOngoing && quest.QuestGiver == hero)
        {
          result = false;
          break;
        }
        quest.OnHeroCanHaveQuestOrIssueInfoIsRequested(hero, ref result);
        if (!result)
          break;
      }
    }

    public override void CanHeroDie(
      Hero hero,
      KillCharacterAction.KillCharacterActionDetail causeOfDeath,
      ref bool result)
    {
      foreach (QuestBase quest in (List<QuestBase>) this.Quests)
      {
        quest.OnHeroCanDieInfoIsRequested(hero, causeOfDeath, ref result);
        if (!result)
          break;
      }
    }

    public override void CanHeroBecomePrisoner(Hero hero, ref bool result)
    {
      foreach (QuestBase quest in (List<QuestBase>) this.Quests)
      {
        quest.OnHeroCanBecomePrisonerInfoIsRequested(hero, ref result);
        if (!result)
          break;
      }
    }

    public override void CanHeroEquipmentBeChanged(Hero hero, ref bool result)
    {
      foreach (QuestBase quest in (List<QuestBase>) this.Quests)
      {
        quest.OnHeroCanBeSelectedInInventoryInfoIsRequested(hero, ref result);
        if (!result)
          break;
      }
    }

    public override void CanHeroLeadParty(Hero hero, ref bool result)
    {
      foreach (QuestBase quest in (List<QuestBase>) this.Quests)
      {
        quest.OnHeroCanLeadPartyInfoIsRequested(hero, ref result);
        if (!result)
          break;
      }
    }

    public override void CanHeroMarry(Hero hero, ref bool result)
    {
      foreach (QuestBase quest in (List<QuestBase>) this.Quests)
      {
        quest.OnHeroCanMarryInfoIsRequested(hero, ref result);
        if (!result)
          break;
      }
    }

    public override void CanMoveToSettlement(Hero hero, ref bool result)
    {
      foreach (QuestBase quest in (List<QuestBase>) this.Quests)
      {
        quest.OnHeroCanMoveToSettlementInfoIsRequested(hero, ref result);
        if (!result)
          break;
      }
    }

    public override void CanBeGovernorOrHavePartyRole(Hero hero, ref bool result)
    {
      foreach (QuestBase quest in (List<QuestBase>) this.Quests)
      {
        quest.OnHeroCanHavePartyRoleOrBeGovernorInfoIsRequested(hero, ref result);
        if (!result)
          break;
      }
    }

    public void AddTrackedObjectForQuest(
      ITrackableCampaignObject trackedObject,
      QuestBase relatedQuest)
    {
      List<QuestBase> questBaseList;
      if (this._trackedObjects.TryGetValue(trackedObject, out questBaseList))
      {
        if (!questBaseList.Contains(relatedQuest))
          questBaseList.Add(relatedQuest);
        else
          Debug.FailedAssert(trackedObject.GetName().ToString() + " already contains quest: " + (object) relatedQuest.Title, "C:\\Develop\\MB3\\Source\\Bannerlord\\TaleWorlds.CampaignSystem\\QuestManager.cs", nameof (AddTrackedObjectForQuest), 362);
      }
      else
        this._trackedObjects.Add(trackedObject, new List<QuestBase>()
        {
          relatedQuest
        });
    }

    public void RemoveTrackedObjectForQuest(
      ITrackableCampaignObject trackedObject,
      QuestBase relatedQuest)
    {
      List<QuestBase> questBaseList;
      if (this._trackedObjects.TryGetValue(trackedObject, out questBaseList))
      {
        if (questBaseList.Contains(relatedQuest))
        {
          questBaseList.Remove(relatedQuest);
          if (questBaseList.Count != 0)
            return;
          this._trackedObjects.Remove(trackedObject);
          Campaign.Current.VisualTrackerManager.RemoveTrackedObject((ITrackableBase) trackedObject);
        }
        else
          Debug.FailedAssert(trackedObject.GetName().ToString() + " is not tracked by quest: " + (object) relatedQuest.Title, "C:\\Develop\\MB3\\Source\\Bannerlord\\TaleWorlds.CampaignSystem\\QuestManager.cs", nameof (RemoveTrackedObjectForQuest), 386);
      }
      else
        Debug.FailedAssert(trackedObject.GetName().ToString() + " does not track any quests.", "C:\\Develop\\MB3\\Source\\Bannerlord\\TaleWorlds.CampaignSystem\\QuestManager.cs", nameof (RemoveTrackedObjectForQuest), 391);
    }

    public void RemoveAllTrackedObjectsForQuest(QuestBase quest)
    {
      List<ITrackableCampaignObject> trackableCampaignObjectList = new List<ITrackableCampaignObject>();
      foreach (KeyValuePair<ITrackableCampaignObject, List<QuestBase>> trackedObject in this.TrackedObjects)
      {
        if (trackedObject.Value.Contains(quest))
          trackableCampaignObjectList.Add(trackedObject.Key);
      }
      foreach (ITrackableCampaignObject trackedObject in trackableCampaignObjectList)
        this.RemoveTrackedObjectForQuest(trackedObject, quest);
    }

    public List<ITrackableCampaignObject> GetAllTrackedObjectsOfAQuest(QuestBase quest)
    {
      List<ITrackableCampaignObject> trackedObjectsOfAquest = new List<ITrackableCampaignObject>();
      foreach (KeyValuePair<ITrackableCampaignObject, List<QuestBase>> trackedObject in this.TrackedObjects)
      {
        if (trackedObject.Value.Contains(quest))
          trackedObjectsOfAquest.Add(trackedObject.Key);
      }
      return trackedObjectsOfAquest;
    }

    public IEnumerable<QuestBase> GetQuestGiverQuests(Hero hero)
    {
      foreach (QuestBase quest in (List<QuestBase>) this.Quests)
      {
        if (quest.IsOngoing && quest.QuestGiver == hero)
          yield return quest;
      }
    }

    public static bool QuestExistInSettlementNotables(QuestBase questBase, Settlement settlement)
    {
      foreach (Hero notable in (List<Hero>) settlement.Notables)
      {
        if (questBase.QuestGiver == notable)
          return true;
      }
      return false;
    }

    public static bool QuestExistInClan(QuestBase questBase, Clan clan)
    {
      foreach (Hero lord in (List<Hero>) clan.Lords)
      {
        if (questBase.QuestGiver == lord)
          return true;
      }
      return false;
    }
  }
}
